package com.epam.training.collection;

import java.util.Arrays;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.RandomAccess;

public class MyArrayList<T> implements MyList<T>, RandomAccess {

	private Object[] elementData;
	public static final int DEFAULT_CAPACITY = 10;
	public static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

	private int size;

	public MyArrayList() {
		elementData = new Object[DEFAULT_CAPACITY];
		size = 0;
	}

	public MyArrayList(MyList<T> c) {
		elementData = c.toArray();
		size = elementData.length;
	}

	public MyArrayList(int initialCapacity) {
		elementData = new Object[initialCapacity];
		size = 0;
	}

	public void trimToSize() {
		if (size < elementData.length) {
			elementData = Arrays.copyOf(elementData, size);
		}
	}

	private void ensureCapacity(int minCapacity) {
		if (minCapacity - elementData.length > 0) {
			int oldCapacity = elementData.length;
			int newCapacity = oldCapacity + (oldCapacity >> 1);
			if (newCapacity - minCapacity < 0) {
				newCapacity = minCapacity;
			}

			if (newCapacity - MAX_ARRAY_SIZE > 0) {
				if (minCapacity < 0) {
					throw new OutOfMemoryError();
				}
				newCapacity = (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE
						: MAX_ARRAY_SIZE;
			}

			elementData = Arrays.copyOf(elementData, newCapacity);
		}
	}

	public void add(T e) {
		ensureCapacity(size + 1);
		elementData[size++] = e;
	}

	public void add(T e, int index) {
		rangeCheck(index);
		ensureCapacity(size + 1);

		int numMoved = size - index - 1;

		if (numMoved > 0) {
			System.arraycopy(elementData, index, elementData, index + 1,
					numMoved);
		}

		elementData[index] = e;

		size++;

	}

	public void addAll(T[] c) {
		ensureCapacity(size + c.length);

		System.arraycopy(c, 0, elementData, size, c.length);
		size += c.length;
	}

	public void addAll(int index, T[] c) {
		rangeCheck(index);
		ensureCapacity(size + c.length);

		int numMoved = size - index;

		if (numMoved > 0) {
			System.arraycopy(elementData, index, elementData, index + c.length,
					numMoved);
		}

		System.arraycopy(c, 0, elementData, index, c.length);
		size += c.length;

	}

	private void rangeCheck(int index) {
		if (index < 0 || index > size) {
			throw new MyIndexOutOfBoundsException();
		}
	}

	@SuppressWarnings("unchecked")
	public T get(int index) {
		rangeCheck(index);
		return (T) elementData[index];
	}

	@SuppressWarnings("unchecked")
	public T remove(int index) {
		rangeCheck(index);

		T old = (T) elementData[index];

		int numMoved = size - index - 1;

		if (numMoved > 0) {
			System.arraycopy(elementData, index + 1, elementData, index,
					numMoved);
		}

		elementData[size--] = null;

		return old;
	}

	public void clear() {
		for (int i = 0; i < size; i++)
			elementData[i] = null;

		size = 0;
	}

	public boolean isEmpty() {
		return size == 0;
	}

	public void set(int index, T e) {
		rangeCheck(index);
		elementData[index] = e;
	}

	public int indexOf(T o) {
		if (o == null) {
			for (int i = 0; i < size; i++) {
				if (elementData[i] == null) {
					return i;
				}
			}
		} else {
			for (int i = 0; i < size; i++) {
				if (o.equals(elementData[i])) {
					return i;
				}
			}
		}

		return -1;
	}

	public int size() {
		return size;
	}

	public Object[] toArray() {
		return Arrays.copyOf(elementData, size);
	}

	public Iterator<T> iterator() {
		return new MyIterator();
	}

	private class MyIterator implements Iterator<T> {

		int cursor;
		int lastReturn;

		MyIterator() {
			lastReturn = -1;
		}

		public boolean hasNext() {
			return cursor != size;
		}

		@SuppressWarnings("unchecked")
		public T next() {
			if (!hasNext()) {
				throw new NoSuchElementException();
			}
			lastReturn = cursor;
			cursor++;

			return (T) elementData[lastReturn];
		}

		public void remove() {
			MyArrayList.this.remove(cursor--);
			lastReturn = -1;
		}
	}
}
